Deployment

Dashboard deployment

Dashboards are typically just static HTML pages so can be deployed to any web server or web host.

Static Rendered a single time (e.g. when underlying data won’t ever change)
Scheduled Rendered on a schedule (e.g. via cron job) to accommodate changing data.
Parameterised Variations of static or scheduled dashboards based on parameters.
Interactive Fully interactive dashboard using Shiny (requires a server for deployment).

Quarto Pub

  • Quarto Pub is a free publishing service for content created with Quarto.
    • Ideal for blogs, course or project websites, books, presentations, and personal hobby sites.
  • It’s important to note that all documents and sites published to Quarto Pub are publicly visible. You should only publish content you wish to share publicly.
  • There are two ways to publish content to Quarto Pub (both are covered in more detail below):
    • Use the quarto publish command to publish content rendered on your local machine (this is the recommend approach when you are getting started).
    • If you are using GitHub, you can use a GitHub Action to automatically render your project and publish the resulting content whenever your code changes.

Important

  • Before attempting your first publish, be sure that you have created a free Quarto Pub account.

Quarto Pub

  • Quarto Pub sites are publicly visible.
    • You can delete any of your site by logging onto your account.
  • Can’t be larger than 100 MB and have a soft limit of 10 GB of bandwidth per month.
  • If you want to authenticate users, host larger sites, or use a custom domain, consider using a professional web publishing service like Netlify instead.

Publish Command

Tip

  • The quarto publish command is the easiest way to publish locally rendered content.
  • From the directory where your project is located, execute the quarto publish command for Quarto Pub:
Terminal
quarto publish quarto-pub
  • If you haven’t published to Quarto Pub before, the publish command will prompt you to authenticate.
  • After confirming that you want to publish, your content will be rendered and deployed, and then a browser opened to view your site.

_publish.yml

  • This file _publish.yml is automatically created (or updated) whenever you execute the quarto publish command, and is located within the project or document directory.
    • The _publish.yml file is used to specify the publishing destination.
    • The service, id, and URL of the published content is specified in _publish.yml.
  • For example:
- source: project
  quarto-pub:
    - id: "5f3abafe-68f9-4c1d-835b-9d668b892001"
      url: "https://njones.quarto.pub/blog"

Tip

If you have an existing Quarto Pub site that you want to publish to, you should manually create a _publish.yml file that looks like the example above, but with the appropriate id and url values for your site.

Note

Account information is not stored in _publish.yml, so it is suitable for checking in to version control and being shared by multiple publishers.

Other publishing channel

Parameters

Parameters

dashboard-r.qmd
---
title: "Parameters"
format: 
  dashboard:
    logo: images/beetle.png
params:
  class: "compact"
---

```{r}
library(ggplot2)
library(dplyr)
mpg <- mpg |>
  filter(class == params$class)
```

## Value boxes {height="25%"}

::: {.valuebox icon="car-front-fill" color="info"}
Class

`{r} params$class`
:::

```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
  filter(cty == min(cty)) |>
  distinct(cty) |>
  pull(cty)

highest_mileage_cty <- mpg |>
  filter(cty == max(cty)) |>
  distinct(cty) |>
  pull(cty)
  
rounded_mean_city_mileage <- mpg |>
  summarize(round(mean(cty), 2)) |>
  pull()
```

```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
  value = paste(lowest_mileage_cty, "mpg")
)
```

```{r}
#| content: valuebox
#| title: "Most efficient"
list(
  icon = "fuel-pump",
  color = "success",
  value = paste(highest_mileage_cty, "mpg")
)
```

::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage

`{r} rounded_mean_city_mileage` mpg
:::

## Plots {height="75%"}

```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
  geom_point()
```

```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
  geom_bar()
```

Parameters

dashboard-py.qmd
---
title: "Parameters"
format: 
  dashboard:
    logo: images/beetle.png
params:
  class: "compact"
---

```{r}
library(ggplot2)
library(dplyr)
mpg <- mpg |>
  filter(class == params$class)
```

## Value boxes {height="25%"}

::: {.valuebox icon="car-front-fill" color="info"}
Class

`{r} params$class`
:::

```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
  filter(cty == min(cty)) |>
  distinct(cty) |>
  pull(cty)

highest_mileage_cty <- mpg |>
  filter(cty == max(cty)) |>
  distinct(cty) |>
  pull(cty)
  
rounded_mean_city_mileage <- mpg |>
  summarize(round(mean(cty), 2)) |>
  pull()
```

```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
  value = paste(lowest_mileage_cty, "mpg")
)
```

```{r}
#| content: valuebox
#| title: "Most efficient"
list(
  icon = "fuel-pump",
  color = "success",
  value = paste(highest_mileage_cty, "mpg")
)
```

::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage

`{r} rounded_mean_city_mileage` mpg
:::

## Plots {height="75%"}

```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
  geom_point()
```

```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
  geom_bar()
```

Parameters

dashboard-py.qmd
---
title: "Parameters"
format: 
  dashboard:
    logo: images/beetle.png
params:
  class: "midsize"
---

```{r}
library(ggplot2)
library(dplyr)
mpg <- mpg |>
  filter(class == params$class)
```

## Value boxes {height="25%"}

::: {.valuebox icon="car-front-fill" color="info"}
Class

`{r} params$class`
:::

```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
  filter(cty == min(cty)) |>
  distinct(cty) |>
  pull(cty)

highest_mileage_cty <- mpg |>
  filter(cty == max(cty)) |>
  distinct(cty) |>
  pull(cty)
  
rounded_mean_city_mileage <- mpg |>
  summarize(round(mean(cty), 2)) |>
  pull()
```

```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
  value = paste(lowest_mileage_cty, "mpg")
)
```

```{r}
#| content: valuebox
#| title: "Most efficient"
list(
  icon = "fuel-pump",
  color = "success",
  value = paste(highest_mileage_cty, "mpg")
)
```

::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage

`{r} rounded_mean_city_mileage` mpg
:::

## Plots {height="75%"}

```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
  geom_point()
```

```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
  geom_bar()
```

Rendering with parameter

Use the -P command line option to vary the parameter:

quarto render dashboard-r.qmd -P class:"suv"

Parameters

dashboard-py.qmd
---
title: "Valueboxes"
format: dashboard
---

```{python}
from plotnine import ggplot, aes, geom_point, geom_bar
from plotnine.data import mpg
```

```{python}
#| tags: [parameters]

car_class = "compact"
```

```{python}
#| label: filter-for-class
filtered_mpg = mpg[mpg['class'] == car_class]
filtered_mpg.reset_index(drop=True, inplace=True)
```

## Value boxes {height="25%"}

::: {.valuebox icon="car-front-fill" color="info"}
Class

`{python} car_class`
:::

```{python}
#| label: calculate-values
lowest_mileage_index = filtered_mpg['cty'].idxmin()
lowest_mileage_car = filtered_mpg.iloc[lowest_mileage_index]
lowest_mileage_cty = filtered_mpg.loc[lowest_mileage_index, 'cty']

highest_mileage_index = filtered_mpg['cty'].idxmax()
highest_mileage_car = filtered_mpg.iloc[highest_mileage_index]
highest_mileage_cty = filtered_mpg.loc[highest_mileage_index, 'cty']

mean_city_mileage = filtered_mpg['cty'].mean()
rounded_mean_city_mileage = round(mean_city_mileage, 2)
```

```{python}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
dict(
  value = str(f"{lowest_mileage_cty} mpg")
)
```

```{python}
#| content: valuebox
#| title: "Most efficient"
dict(
  icon = "fuel-pump",
  color = "success",
  value = str(f"{highest_mileage_cty} mpg")
)
```

::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage

`{python} str(rounded_mean_city_mileage)` mpg
:::

## Plots {height="75%"}

```{python}
#| title: Highway vs. city mileage
(
    ggplot(filtered_mpg, aes(x = "cty", y = "hwy"))
    + geom_point()
)
```

```{python}
#| title: Drive types
(
    ggplot(filtered_mpg, aes(x = "drv"))
    + geom_bar()
)
```

Interactivity

Interactivity with Shiny

  • The Shiny package (for R and for Python) provides an easy way to build web applications with R.

  • Quarto dashboards can include embedded Shiny components (e.g., a plot with sliders that control its inputs) to add interactivity.

  • Ladder of complexity/customization: Quarto dashboard > Quarto dashboard with Shiny components > Shiny dashboard

Deployment of interactive dashboards

Server

On-Prem

Serverless

Using Pyodide

[Example]

Using ShinyLive

Other interactivity

  • Observable JS for client-side interactivity using the Observable dialect of JavaScript.

  • Jupyter Widgets or htmlwidgets for client-side interactivity based on standard Python and R JavaScript widget frameworks.

Your turn

Start

R: Work on your best version of olympicdash-r-3.qmd or olympicdash-r-4.qmd in your cloned olympicdash folder.

Python: Work on your best version of olympicdash-py-3.qmd or olympicdash-py-4.qmd in your cloned olympicdash folder.

Goal

Deploy your latest dashboard to QuartoPub.

Stretch goal: Add parameter and/or interactivity to your dashboard for narrowing down to or picking a year.

  • For example: in R, you can use the plotly package & applying ggplotly to give some interactivity to your ggplots.
10:00

Thanks!